﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yto.Common.Dependency;
using Yto.EF.DbCtx;
using Yto.Unitwork;

namespace Yto.EF.UW
{
    public class EFUnitwork : UnitworkBase ,ITransient
    {
        private IEFTransaction _transaction;
        private IDbResolver _dbResolver;
        private ICtnResolver _ctnResolver;
        private DbContext _currentDbContext;
        private IDictionary<string, DbContext> dicDbContext;

        public EFUnitwork(
            IEFTransaction eFTransaction, 
            IDbResolver dbResolver, 
            IConnStringResolver connStringResolver,
            ICtnResolver ctnResolver
            ): base(connStringResolver)
        {
            _transaction = eFTransaction;
            _dbResolver = dbResolver;
            _ctnResolver = ctnResolver;
            dicDbContext = new Dictionary<string, DbContext>();
        }
        protected override void BeginUnit()
        {
            if (_options.IsTransactional.HasValue &&_options.IsTransactional.Value)
            {
                _transaction.Init(_options);
            }
        }

        internal T GetDbContext<T>() where T : DbContext
        {
            ConnStringArgs args = new ConnStringArgs();
            args["DbType"] = ConfigurationManager.AppSettings["DbType"];
            var connString = _connStringResolver.GetConnStringName(args);

            var key = typeof(T).FullName + "#" + connString;

            if (!dicDbContext.TryGetValue(key, out _currentDbContext))
            {
                if(_options.IsTransactional.HasValue&& _options.IsTransactional.Value)
                {
                    _currentDbContext = _transaction.GetDbContext<T>(connString, _dbResolver);
                }
                else
                {
                    _currentDbContext = _dbResolver.Resolver<T>(connString);
                }
                
                dicDbContext[key] = _currentDbContext;
            }

            return (T)_currentDbContext;
        }

        protected override void CompleteUnit()
        {
            SaveChanges();
            if (_options.IsTransactional.HasValue && _options.IsTransactional.Value)
            {
                _transaction.Commit();
            }
        }

        protected override void DisposeUnit()
        {
            if (_options.IsTransactional.HasValue && _options.IsTransactional.Value)
            {
                _transaction.Dispose(_ctnResolver);
            }
            else
            {
                if (_currentDbContext != null)
                {
                    _dbResolver.Realse(_currentDbContext);
                }
            }
            dicDbContext.Clear();
        }

        protected override void SaveChanges()
        {
            if (_currentDbContext != null)
            {
                _currentDbContext.SaveChanges();
            }
        }

        protected override async Task CompleteUnitAsync()
        {
            await SaveChangesAsync();
            if (_options.IsTransactional == true)
            {
                _transaction.Commit();
            }
        }

        protected override async Task SaveChangesAsync()
        {
            if(_currentDbContext != null)
            {
                await _currentDbContext.SaveChangesAsync();
            }
        }
    }
}
